﻿using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using GalaSoft.MvvmLight.Messaging;
using IssueVision.Common;
using IssueVision.EntityModel;
using SL.IdentityModel.Services;

namespace IssueVision.ViewModel
{
    [Export(ViewModelTypes.AllIssuesViewModel, typeof(ViewModelBase))]
    [PartCreationPolicy(CreationPolicy.NonShared)]
    public class AllIssuesViewModel : ViewModelBase
    {
        #region "Private Data Members"
        private IIssueVisionModel _issueVisionModel;
        private bool _isDeleteOperation;
        #endregion "Private Data Members"

        #region "Constructor"
        [ImportingConstructor]
        public AllIssuesViewModel(IIssueVisionModel issueVisionModel)
        {
            _issueVisionModel = issueVisionModel;

            // set up event handling
            _issueVisionModel.SaveChangesCompleted += _issueVisionModel_SaveChangesCompleted;
            _issueVisionModel.GetAllIssuesCompleted += _issueVisionModel_GetAllIssuesCompleted;
            _issueVisionModel.PropertyChanged += _issueVisionModel_PropertyChanged;

            // load all issues
            _issueVisionModel.GetAllIssuesAsync();
        }
        #endregion "Constructor"

        #region "ICleanup interface implementation"
        public override void Cleanup()
        {
            if (_issueVisionModel != null)
            {
                // unregister all events
                _issueVisionModel.SaveChangesCompleted -= _issueVisionModel_SaveChangesCompleted;
                _issueVisionModel.GetAllIssuesCompleted -= _issueVisionModel_GetAllIssuesCompleted;
                _issueVisionModel.PropertyChanged -= _issueVisionModel_PropertyChanged;
                // set CurrentIssue back to null
                CurrentIssue = null;
                // set _issueVisionModel to null
                _issueVisionModel = null;
            }
            // set properties back to null
            _allIssues = null;
            // unregister any messages for this ViewModel
            base.Cleanup();
        }
        #endregion "ICleanup interface implementation"

        #region "Public Properties"

        private ObservableCollection<Issue> _allIssues;

        public CollectionViewSource AllIssuesSource { get; private set; }

        public Issue CurrentIssue
        {
            get
            {
                return _issueVisionModel != null ? _issueVisionModel.CurrentEditIssue : null;
            }
            private set
            {
                if (_issueVisionModel != null)
                {
                    if (!ReferenceEquals(_issueVisionModel.CurrentEditIssue, value))
                    {
                        // update CurrentEditIssue on the model
                        _issueVisionModel.CurrentEditIssue = value;
                        RaisePropertyChanged("CurrentIssue");
                    }
                }
            }
        }

        #endregion "Public Properties"

        #region "Public Commands"

        private RelayCommand<SelectionChangedEventArgs> _selectionChangedCommand;

        public RelayCommand<SelectionChangedEventArgs> SelectionChangedCommand
        {
            get
            {
                if (_selectionChangedCommand == null)
                {
                    _selectionChangedCommand = new RelayCommand<SelectionChangedEventArgs>(
                        e => OnSelectionChangedCommand(e));
                }
                return _selectionChangedCommand;
            }
        }

        private void OnSelectionChangedCommand(SelectionChangedEventArgs e)
        {
            if (e.AddedItems.Count == 1)
            {
                // cancel any changes before editing another issue
                if (_issueVisionModel.HasChanges)
                {
                    _issueVisionModel.RejectChanges();
                }
                var enumerator = e.AddedItems.GetEnumerator();
                enumerator.MoveNext();
                CurrentIssue = (Issue) enumerator.Current;
                // edit the new selected issue
                AppMessages.EditIssueMessage.Send(CurrentIssue);
            }
            else if (e.RemovedItems.Count == 1)
            {
                // cancel any changes before editing another issue
                if (_issueVisionModel.HasChanges)
                {
                    _issueVisionModel.RejectChanges();
                }
                CurrentIssue = null;
                AppMessages.EditIssueMessage.Send(CurrentIssue);
                // refresh DeleteCommand
                DeleteCommand.RaiseCanExecuteChanged();
            }
        }

        private RelayCommand _deleteCommand;

        public RelayCommand DeleteCommand
        {
            get
            {
                if (_deleteCommand == null)
                {
                    _deleteCommand = new RelayCommand(
                        () => OnDeleteCommand(),
                        () => (_issueVisionModel != null) && 
                            (_issueVisionModel.CurrentEditIssue != null) && 
                            !(_issueVisionModel.HasChanges));
                }
                return _deleteCommand;
            }
        }

        private void OnDeleteCommand()
        {
            try
            {
                if (!_issueVisionModel.IsBusy)
                {
                    // cancel any changes before deleting a user
                    if (_issueVisionModel.HasChanges)
                    {
                        _issueVisionModel.RejectChanges();
                    }

                    // ask to confirm deleting the current issue
                    DialogMessage dialogMessage = new DialogMessage(
                        this,
                        CommonResources.DeleteCurrentIssueMessageBoxText,
                        s =>
                        {
                            if (s == MessageBoxResult.OK)
                            {
                                // if confirmed, remove any related attributes & files
                                if (_issueVisionModel.CurrentEditIssue.Attributes != null)
                                {
                                    foreach (EntityModel.Attribute item in _issueVisionModel.CurrentEditIssue.Attributes.ToList())
                                        item.MarkAsDeleted();
                                }
                                if (_issueVisionModel.CurrentEditIssue.Files != null)
                                {
                                    foreach (File item in _issueVisionModel.CurrentEditIssue.Files.ToList())
                                        item.MarkAsDeleted();
                                }
                                // and remove the CurrentIssue
                                _issueVisionModel.CurrentEditIssue.MarkAsDeleted();
                                _isDeleteOperation = true;
                                _issueVisionModel.SaveChangesAsync();
                            }
                        })
                    {
                        Button = MessageBoxButton.OKCancel,
                        Caption = CommonResources.ConfirmMessageBoxCaption
                    };

                    AppMessages.PleaseConfirmMessage.Send(dialogMessage);
                }
            }
            catch (Exception ex)
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(ex);
            }
        }

        private RelayCommand _refreshCommand;

        public RelayCommand RefreshCommand
        {
            get
            {
                if (_refreshCommand == null)
                {
                    _refreshCommand = new RelayCommand(
                        () => OnRefreshCommand(),
                        () => (_issueVisionModel != null) && !(_issueVisionModel.HasChanges));
                }
                return _refreshCommand;
            }
        }

        private void OnRefreshCommand()
        {
            try
            {
                if (!_issueVisionModel.IsBusy)
                {
                    // re-load all issues
                    _issueVisionModel.GetAllIssuesAsync();
                }
            }
            catch (Exception ex)
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(ex);
            }
        }

        private RelayCommand _submitChangeCommand;

        public RelayCommand SubmitChangeCommand
        {
            get
            {
                if (_submitChangeCommand == null)
                {
                    _submitChangeCommand = new RelayCommand(
                        () => OnSubmitChangeCommand(),
                        () => (_issueVisionModel != null) && (_issueVisionModel.HasChanges));
                }
                return _submitChangeCommand;
            }
        }

        private void OnSubmitChangeCommand()
        {
            try
            {
                if (!_issueVisionModel.IsBusy)
                {
                    if (CurrentIssue != null)
                    {
                        // if status is Open, AssignedToID should be null
                        if (CurrentIssue.StatusID == IssueVisionServiceConstant.OpenStatusID)
                        {
                            CurrentIssue.AssignedToID = null;
                        }
                        // set ResolutionDate and ResolvedByID based on ResolutionID
                        if (CurrentIssue.ResolutionID == null || CurrentIssue.ResolutionID == 0)
                        {
                            CurrentIssue.ResolutionDate = null;
                            CurrentIssue.ResolvedByID = null;
                        }
                        else
                        {
                            if (CurrentIssue.ResolutionDate == null)
                                CurrentIssue.ResolutionDate = DateTime.Now;
                            if (CurrentIssue.ResolvedByID == null)
                                CurrentIssue.ResolvedByID = ClaimsIdentitySessionManager.Current.User.Identity.Name;
                        }
                        if (CurrentIssue.TryValidate())
                            _issueVisionModel.SaveChangesAsync();
                    }
                }
            }
            catch (Exception ex)
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(ex);
            }
        }

        private RelayCommand _cancelChangeCommand;

        public RelayCommand CancelChangeCommand
        {
            get
            {
                if (_cancelChangeCommand == null)
                {
                    _cancelChangeCommand = new RelayCommand(
                        () => OnCancelChangeCommand(),
                        () => (_issueVisionModel != null) && (_issueVisionModel.HasChanges));
                }
                return _cancelChangeCommand;
            }
        }

        private void OnCancelChangeCommand()
        {
            try
            {
                if (!_issueVisionModel.IsBusy)
                {
                    // ask to confirm canceling the current issue in edit
                    DialogMessage dialogMessage = new DialogMessage(
                        this,
                        CommonResources.CancelCurrentIssueMessageBoxText,
                        s =>
                        {
                            if (s == MessageBoxResult.OK)
                            {
                                // if confirmed, cancel this issue
                                _issueVisionModel.RejectChanges();
                            }
                        })
                    {
                        Button = MessageBoxButton.OKCancel,
                        Caption = CommonResources.ConfirmMessageBoxCaption
                    };

                    AppMessages.PleaseConfirmMessage.Send(dialogMessage);
                }
            }
            catch (Exception ex)
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(ex);
            }
        }

        #endregion "Public Commands"

        #region "Private Methods"

        /// <summary>
        /// Event handler for SaveChangesCompleted
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _issueVisionModel_SaveChangesCompleted(object sender, ResultArgs<string> e)
        {
            bool isDeleteOperation = _isDeleteOperation;
            _isDeleteOperation = false;

            if (!e.HasError)
            {
                // check whether there is any warning message returned
                if (!string.IsNullOrEmpty(e.Results))
                {
                    if (isDeleteOperation)
                    {
                        // if the delete operation comes back with some warning
                        // message, we need to rollback.
                        _issueVisionModel.CurrentEditIssue.RejectChanges();
                        if (_issueVisionModel.CurrentEditIssue.Attributes != null)
                        {
                            foreach (EntityModel.Attribute item in _issueVisionModel.CurrentEditIssue.Attributes)
                                item.RejectChanges();
                        }
                        if (_issueVisionModel.CurrentEditIssue.Files != null)
                        {
                            foreach (File item in _issueVisionModel.CurrentEditIssue.Files)
                                item.RejectChanges();
                        }
                    }
                    // display the warning message
                    DialogMessage dialogMessage = new DialogMessage(
                        this,
                        e.Results,
                        null)
                    {
                        Button = MessageBoxButton.OK,
                        Caption = CommonResources.WarningMessageBoxCaption
                    };

                    AppMessages.StatusUpdateMessage.Send(dialogMessage);
                }
                else if (isDeleteOperation)
                {
                    // delete is successful, we need to remove the item from the client side
                    _allIssues.Remove(_issueVisionModel.CurrentEditIssue);
                }
            }
            else
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(e.Error);
            }
        }

        /// <summary>
        /// Event handler for GetAllIssuesCompleted
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _issueVisionModel_GetAllIssuesCompleted(object sender, ResultsArgs<Issue> e)
        {
            if (!e.HasError)
            {
                // cancel any changes before setting AllIssuesSource
                if (_issueVisionModel.HasChanges)
                {
                    _issueVisionModel.RejectChanges();
                }

                _allIssues = new ObservableCollection<Issue>(e.Results.OrderBy(g => g.StatusID).ThenBy(g => g.Priority));

                AllIssuesSource = new CollectionViewSource {Source = _allIssues};
                AllIssuesSource.View.CurrentChanging += View_CurrentChanging;
                // notify that AllIssuesSource has changed
                RaisePropertyChanged("AllIssuesSource");

                // set the first row as the current issue
                if (_allIssues.Count >= 1)
                {
                    CurrentIssue = _allIssues.First();
                    AppMessages.EditIssueMessage.Send(CurrentIssue);
                }
            }
            else
            {
                // notify user if there is any error
                AppMessages.RaiseErrorMessage.Send(e.Error);
            }
        }

        /// <summary>
        /// Event handler for CurrentChanging
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void View_CurrentChanging(object sender, CurrentChangingEventArgs e)
        {
            MessageBoxResult theResult = MessageBoxResult.OK;

            if (_issueVisionModel.HasChanges)
            {
                // ask to confirm canceling the current issue in edit
                DialogMessage dialogMessage = new DialogMessage(
                    this,
                    CommonResources.CancelCurrentIssueMessageBoxText,
                    s => theResult = s)
                {
                    Button = MessageBoxButton.OKCancel,
                    Caption = CommonResources.ConfirmMessageBoxCaption
                };

                AppMessages.PleaseConfirmMessage.Send(dialogMessage);

                if (theResult == MessageBoxResult.Cancel)
                {
                    e.Cancel = true;
                }
            }
        }

        /// <summary>
        /// Event handler for PropertyChanged
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void _issueVisionModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName.Equals("HasChanges"))
            {
                DeleteCommand.RaiseCanExecuteChanged();
                RefreshCommand.RaiseCanExecuteChanged();
                SubmitChangeCommand.RaiseCanExecuteChanged();
                CancelChangeCommand.RaiseCanExecuteChanged();
            }
        }

        #endregion "Private Methods"
    }
}